import os
import platform
import subprocess
import requests, threading
from dataclasses import dataclass
from dacite import from_dict, Config
import shlex
from typing import Dict, Any

MOJANG_VERSION_MANIFEST = "https://piston-meta.mojang.com/mc/game/version_manifest.json"


@dataclass
class Latest:
    release: str
    snapshot: str


@dataclass
class MCVersion:
    id: str
    type: str
    url: str
    time: str
    releaseTime: str


@dataclass
class MCVersionList:
    latest: Latest
    versions: list[MCVersion]


@dataclass
class ModInfo:
    id: str
    version: str
    name: str
    description: str
    author: list[str]
    depends: dict[str, str]


@dataclass
class Artifact:
    path: str
    sha1: str
    url: str


@dataclass
class Downloads:
    artifact: Artifact


@dataclass
class Library:
    downloads: Downloads


@dataclass
class AssetIndex:
    id: str
    sha1: str
    totalSize: int
    url: str


@dataclass
class Client:
    sha1: str
    url: str


@dataclass
class VersionManifest:
    downloads: Dict[str, Client]
    id: str
    libraries: list[Library]
    assetIndex: AssetIndex

@dataclass
class Obj:
    hash: str
    size: int

@dataclass
class Resources:
    objects: dict[str, Obj]

def get_version_manifest(url: str) -> VersionManifest:
    response = requests.get(url)
    return from_dict(
        data_class=VersionManifest, data=response.json(), config=Config(strict=False)
    )


def to_libraries_list(version_manifest: VersionManifest) -> list[Artifact]:
    return [library.downloads.artifact for library in version_manifest.libraries]


def get_mc_version_list() -> MCVersionList:
    response = requests.get(MOJANG_VERSION_MANIFEST)
    return from_dict(
        data_class=MCVersionList, data=response.json(), config=Config(strict=False)
    )

def get_objs_list(url: str) -> list[Obj]:
    response = requests.get(url)
    r = from_dict(
        data_class=Resources, data=response.json(), config=Config(strict=False))
    return list(r.objects.values())

def to_download_arts_list(objs: list[Obj]) -> list[Artifact]:
    artis = []
    for obj in objs:
        prefix = obj.hash[:2]
        path = f"{prefix}/{obj.hash}"
        url = f"https://resources.download.minecraft.net/{path}"
        arti = Artifact(
                path,obj.hash,url
        )
        artis.append(arti)
    return artis

def download_client(prism_launcher_path:str,version_manifest: VersionManifest):
    url = version_manifest.downloads["client"].url
    path = os.path.join(prism_launcher_path, "libraries/com/mojang/minecraft", version_manifest.id, f"minecraft-{version_manifest.id}-client.jar")
    os.makedirs(os.path.dirname(path), exist_ok=True)
    response = requests.get(url)
    with open(path, "wb") as f:
        f.write(response.content)
    print(f"Downloaded {path}")

def get_assets_url(version_manifest: VersionManifest) -> str:
    return version_manifest.assetIndex.url

def main():
    prism_path = get_prism_launcher_path()
    version_list = get_mc_version_list()
    print(to_version_str_list(version_list))
    user_input = input("Enter version you want download: ")
    version_manifest = get_version_manifest(
        get_version_download_url(user_input, version_list)
    )
    artifacts = to_libraries_list(version_manifest)
    write_aria2_script(f"{prism_path}/libraries", artifacts)
    y = input("Start download libs? (y/n): ")
    if y.lower() == "y":
        start_aria2("download.txt")
    #download_client(prism_path,version_manifest)
    assets_url = get_assets_url(version_manifest)
    objs = get_objs_list(assets_url)
    arts = to_download_arts_list(objs)
    write_aria2_script(f"{prism_path}/assets/objects", arts)
    y = input("Start download assets? (y/n): ")
    if y.lower() == "y":
        start_aria2("download.txt")



def get_version_download_url(version: str, version_list: MCVersionList) -> str:
    target = next((v for v in version_list.versions if v.id == version), None)
    if target is None:
        raise ValueError(f"Version {version} not found")
    return target.url


def to_version_str_list(version_list: MCVersionList) -> list[str]:
    return [version.id for version in version_list.versions]


def start_aria2(script: str):
    cmd = [
        "aria2c",
        "--continue=true",
        "--check-integrity=true",
        "--max-tries=16",
        "--retry-wait=5",
        "--log-level=warn",
        "--console-log-level=error",
        "--summary-interval=5",
        "-x", "10",          # 单文件最大 10 线程
        "-s", "10",          # 同时最多 10 个文件
        "-j", "10",          # 从输入文件中并发下载 10 个任务
        "-i", script
    ]
    subprocess.run(cmd)


def write_aria2_script(
    download_path: str, artifacts: list[Artifact], output: str = "download.txt"
):
    with open(output, "w", encoding="utf-8") as f:
        for artifact in artifacts:
            f.write(
                f"{artifact.url} \n"
                f"  dir={download_path} \n"
                f"  out={artifact.path} \n"
                f"  checksum=sha-1={artifact.sha1}\n"
            )


def get_prism_launcher_path() -> str:
    system = platform.system()
    home = os.path.expanduser("~")

    if system == "Windows":
        return os.path.join(home, "AppData", "Roaming", "PrismLauncher")
    elif system == "Darwin":  # macOS
        return os.path.join(home, "Library", "Application Support", "PrismLauncher")
    else:  # Linux & BSD
        # 优先看官方 Flatpak 路径，再回退到普通 AppImage / 原生包
        flatpak = os.path.join(
            home,
            ".var",
            "app",
            "org.prismlauncher.PrismLauncher",
            "data",
            "PrismLauncher",
        )
        if os.path.isdir(flatpak):
            return flatpak
        return os.path.join(home, ".local", "share", "PrismLauncher")
